﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace NExplorer
{
	public class FileOperations : IService
	{
		#region pinvoke stuff

		[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
		static extern int SHFileOperation([In] ref SHFILEOPSTRUCT lpFileOp);

		[DllImport("shell32.dll")]
		private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

		private const int FO_COPY = 2;
		private const int FO_DELETE = 3;
		private const int FOF_ALLOWUNDO = 0x40;
		private const uint SHGFI_ICON = 0x100;
		private const uint SHGFI_TYPENAME = 0x400;

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
		public struct SHFILEOPSTRUCT
		{
			public IntPtr hwnd;
			[MarshalAs(UnmanagedType.U4)]
			public int wFunc;
			public string pFrom;
			public string pTo;
			public short fFlags;
			[MarshalAs(UnmanagedType.Bool)]
			public bool fAnyOperationsAborted;
			public IntPtr hNameMappings;
			public string lpszProgressTitle;
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct SHFILEINFO
		{
			public IntPtr hIcon;
			public IntPtr iIcon;
			public uint dwAttributes;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
			public string szDisplayName;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
			public string szTypeName;
		}

		#endregion

		//private readonly Dictionary<LowerCaseString, Icon>		_fileIcons = new Dictionary<LowerCaseString, Icon>();
		//private readonly Dictionary<LowerCaseString, string>	_fileDescriptions = new Dictionary<LowerCaseString, string>();

		public void SendItemsToRecycleBin(ICollection<FileSystemInfo> fileSystemInfos)
		{
			string[] names = fileSystemInfos.ToArray(info => info.FullName);

			SHFILEOPSTRUCT fileop = new SHFILEOPSTRUCT();
			fileop.wFunc = FO_DELETE;
			fileop.pFrom = string.Join("\0", names) + '\0';
			fileop.fFlags = FOF_ALLOWUNDO;

			SHFileOperation(ref fileop);
		}

		public void CopyItemsToClipboard(ICollection<FileSystemInfo> fileSystemInfos)
		{
			StringCollection names = new StringCollection();
			fileSystemInfos.ForEach(info => names.Add(info.FullName));

			Clipboard.SetFileDropList(names);
		}

		public void Rename(FileSystemInfo fileSystemInfo, string newName)
		{
			FileSystemInfo newInfo;
			//string parentPath;

			if (fileSystemInfo is DirectoryInfo)
			{
				DirectoryInfo dirInfo = (DirectoryInfo)fileSystemInfo;
				//parentPath = dirInfo.Parent.FullName;
				newInfo = new DirectoryInfo(Path.Combine(dirInfo.Parent.FullName, newName));
				dirInfo.MoveTo(newInfo.FullName);
			}
			else if (fileSystemInfo is FileInfo)
			{
				FileInfo fileInfo = (FileInfo)fileSystemInfo;
				//parentPath = fileInfo.Directory.FullName;
				newInfo = new FileInfo(Path.Combine(fileInfo.Directory.FullName, newName));
				fileInfo.MoveTo(newInfo.FullName);
			}
			else
			{
				throw new NotSupportedException();
			}

			//FileSystemWatcher fsWatcher = new FileSystemWatcher(parentPath, fileSystemInfo.Name);
			//fsWatcher.Renamed += WhenFileRenamed;
			//fsWatcher.EnableRaisingEvents = true;
		}

		//private static void WhenFileRenamed(object sender, RenamedEventArgs e)
		//{

		//    FileSystemWatcher fsWatcher = (FileSystemWatcher)sender;
		//    fsWatcher.EnableRaisingEvents = false;
		//    fsWatcher.Dispose();

		//    if (File.Exists(e.FullPath))
		//    {
		//        ServiceManager.Explorer.SelectedItems = new FileInfo(e.FullPath).ToArray();
		//    }
		//    else if (Directory.Exists(e.FullPath))
		//    {
		//        ServiceManager.Explorer.SelectedItems = new DirectoryInfo(e.FullPath).ToArray();
		//    }
		//    else
		//    {
		//        // Oh well..
		//    }
		//}

		public void GetIconAndDescription(FileSystemInfo fileSystemInfo, out Icon icon, out string description)
		{
			SHFILEINFO info = new SHFILEINFO();
			SHGetFileInfo(fileSystemInfo.FullName, 0, ref info, (uint)Marshal.SizeOf(info), SHGFI_TYPENAME | SHGFI_ICON | 1);

			description = info.szTypeName;

			try
			{
				// May fail if file no longer exists. Prefer to handle this exceptional case
				// rather than take the overhead of checking for file every call, of which there are many.
				icon = Icon.FromHandle(info.hIcon);
			}
			catch
			{
				icon = null;
			}
		}

		public void PasteItemsFromClipboard()
		{
			IList<FileSystemInfo> infos = ServiceManager.Explorer.SelectedItems;

			if (infos.Count == 1 && infos[0] is DirectoryInfo)
			{
				StringCollection filesToCopy = Clipboard.GetFileDropList();
				string[] names = filesToCopy.ToArray<string>();

				SHFILEOPSTRUCT fileop = new SHFILEOPSTRUCT();
				fileop.wFunc = FO_COPY;
				fileop.pFrom = string.Join("\0", names) + '\0';
				fileop.pTo = infos[0].FullName + "\0\0";
				fileop.fFlags = FOF_ALLOWUNDO;

				SHFileOperation(ref fileop);
			}
			else
			{
				MessageBox.Show("A single folder must be selected.", "Paste", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
	}
}
